package com.fc.remoting.handler;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fc.remoting.event.ChannelEvent;
import com.fc.remoting.event.ChannelEvent.Type;
import com.fc.remoting.event.ChannelEventCause;
import com.fc.remoting.event.ChannelEventListener;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;

public class ChannelManagerHandler extends ChannelHandlerAdapter {

	private static final Logger logger = LoggerFactory
			.getLogger(ChannelManagerHandler.class);
	public static AttributeKey<ConcurrentHashMap<String, Object>> ATT_USER_DATA = AttributeKey
			.valueOf("user_data");
	public static AttributeKey<Object> ATT_IDENTITY = AttributeKey
			.valueOf("identity");
	AttributeKey<Boolean> ATT_PROCEED = AttributeKey.valueOf("proceed");
	//http链接
	private static Map<Object, Channel> httpIdentities = new ConcurrentHashMap<Object, Channel>();
	//tcp链接
	private static Map<Object,Channel> mobChannels = new ConcurrentHashMap<Object,Channel>();
	
	//pc 版tcp链接
	private static Map<Object,Channel> pcChannels = new ConcurrentHashMap<Object,Channel>();
	
	private static Map<String, Channel> anonymous = new ConcurrentHashMap<String, Channel>();
	private static Map<Object, Channel> closeds = new ConcurrentHashMap<Object, Channel>();
	
	private static Map<Type, ChannelEventListener> listeners = new ConcurrentHashMap<Type, ChannelEventListener>();
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		Channel channel = ctx.channel();
		try{
			Object id = channel.attr(ATT_IDENTITY).get();
			fireClosedEvent(id, ChannelEventCause.NORMAL, channel);
		}catch(Exception e){
			logger.error(e.getMessage());
		}
		
		anonymous.remove(channel.id().asLongText());
		logger.info("Inactive:" + channel.id().asLongText());
		super.channelInactive(ctx);
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		Channel session = ctx.channel();
		anonymous.put(session.id().asLongText(), session);
		logger.info("Active:" + session.id().asLongText());
		super.channelActive(ctx);
	}
	
	/**
	 * 添加http链接到链接池
	 * @param id
	 * @param channel
	 */
	public static void putHttpChannel(String id,Channel channel){
		channel.attr(ATT_IDENTITY).set(id);
		httpIdentities.put(id, channel);
	}
	
	/**
	 * 添加移动端链接到链接池
	 * @param id
	 * @param channel
	 */
	public static void putMobChannel(String id,Channel channel){
		channel.attr(ATT_IDENTITY).set(id);
		mobChannels.put(id, channel);
	}
	
	/**
	 * 添加pc端链接到链接池
	 * @param id
	 * @param channel
	 */
	public static void putPcChannel(String id,Channel channel){
		channel.attr(ATT_IDENTITY).set(id);
		pcChannels.put(id, channel);
	}
	
//	/**
//	 * tcp 
//	 * @param request
//	 * @param ids
//	 */
//	public static void send(JSONObject request, Object... ids) {
//		if (ids == null || ids.length == 0) {
//			return;
//		}
//
//		for (Object id : ids) {
//			Channel session = mobChannels.get(id);
//			if (session == null) {
//				continue;
//			}
//			session.writeAndFlush(request);
//		}
//	}

//	public static void sendAll(JSONObject request) {
//		sendAllIdentified(request);
//	}
//	
//	/**
//	 * tcp
//	 * @param request
//	 */
//	public static void sendAllIdentified(JSONObject request) {
//		for (Channel channel : mobChannels.values()) {
//			channel.writeAndFlush(request);
//		}
//	}

//	public static void sendAllAnonymous(JSONObject request) {
//		for (Channel channel : anonymous.values()) {
//			channel.write(request);
//			channel.flush();
//		}
//	}
	
	/**
	 * 判断是否在线
	 * @param ids
	 * @return
	 */
	public static boolean isOnline(Object... ids) {
		for (Object id : ids) {
			if (!mobChannels.containsKey(id) && !pcChannels.containsKey(id)) {
				return false;
			}
		}
		return true;
	}
	
	public static boolean isMobileOnline(Object id){
		return mobChannels.containsKey(id);
	}
	
	public static boolean isPCOnline(Object id){
		return pcChannels.containsKey(id);
	}
	
	/**
	 * 获取在线列表
	 * @return
	 */
	public static Collection<Object> getOnlineIdentities() {
		HashSet<Object> result = new HashSet<Object>();
		result.addAll(mobChannels.keySet());
		result.addAll(pcChannels.keySet());
		return result;
	}
	
	/**
	 * 断开移动端链接
	 * @param cause
	 * @param ids
	 * @return
	 */
	public static void kickMobileChannel(int cause, Object... ids) {
		for (Object id : ids) {
			
			//移除移动端链接
			Channel mobChannel = mobChannels.get(id);
			if (mobChannel == null) {
				continue;
			}
			removeMobChannel(mobChannel);
			fireClosedEvent(id, cause, mobChannel);
		}
	}
	
	/**
	 * 断开PC端链接
	 * @param cause
	 * @param ids
	 * @return
	 */
	public static void kickPCChannel(int cause, Object... ids) {
		for (Object id : ids) {
			
			//移除pc端链接
			Channel pcChannel = pcChannels.get(id);
			if (pcChannel == null) {
				continue;
			}
			removePcChannel(pcChannel);
			fireClosedEvent(id, cause, pcChannel);
		}
	}
	
	/**
	 * 断开链接
	 * @param cause
	 * @param ids
	 * @return
	 */
	public static void kick(int cause, Object... ids) {
		for (Object id : ids) {
			
			//移除移动端链接
			Channel mobChannel = mobChannels.get(id);
			if (mobChannel == null) {
				continue;
			}
			removeMobChannel(mobChannel);
			fireClosedEvent(id, cause, mobChannel);
			
			//移除pc端链接
			Channel pcChannel = pcChannels.get(id);
			if (pcChannel == null) {
				continue;
			}
			removePcChannel(pcChannel);
			fireClosedEvent(id, cause, pcChannel);
		}
	}
	
	/**
	 * 断开所有链接
	 * @param cause
	 * @return
	 */
	public void kickAll(int cause) {
		//移除移动端链接
		for (Entry<Object, Channel> entry : mobChannels.entrySet()) {
			Channel session = entry.getValue();
			removeMobChannel(session);
			session.close();
		}
		
		//移除pc端链接
		for (Entry<Object, Channel> entry : pcChannels.entrySet()) {
			Channel session = entry.getValue();
			removePcChannel(session);
			session.close();
		}
	}
	
	/**
	 * 更新连接
	 * @param cause
	 */
	public static void updateChannel(int cause){
		//移除移动端链接
		for (Entry<Object, Channel> entry : mobChannels.entrySet()) {
			Object id = entry.getKey();
			Channel session = entry.getValue();
			removeMobChannel(session);
			fireUpdateChannelEvent(id, cause, session);
		}
		
		//移除pc端链接
		for (Entry<Object, Channel> entry : pcChannels.entrySet()) {
			Object id = entry.getKey();
			Channel session = entry.getValue();
			removePcChannel(session);
			fireUpdateChannelEvent(id, cause, session);
		}
	}
	
	/**
	 * 获取移动端链接
	 * @param id
	 * @return
	 */
	public static Channel getMobChannel(Object id) {
		Channel result = mobChannels.get(id);
		if (result != null) {
			return result;
		}
		result = closeds.get(id);
		return result;
	}
	
	/**
	 * 获取pc端链接
	 * @param id
	 * @return
	 */
	public static Channel getPcChannel(Object id) {
		Channel result = pcChannels.get(id);
		if (result != null) {
			return result;
		}
		result = closeds.get(id);
		return result;
	}
	
	/**
	 * 获取http链接
	 * @param id
	 * @return
	 */
	public static Channel getHttpSession(Object id){
		return httpIdentities.get(id);
	}

	public Collection<Channel> getAnonymous() {
		return anonymous.values();
	}
	
	/**
	 * 当前在线人数
	 * @param includeHttps
	 * @return
	 */
	public static int count(boolean includeHttps) {
		int result = 0;
		result += mobChannels.size();
		if (includeHttps) {
			result += httpIdentities.size();
		}
		return result;
	}

	/**
	 * 获取链接id
	 * @param session
	 * @return
	 */
	public static boolean onIdentified(Channel channel) {
		Object identity = channel.attr(ATT_IDENTITY).get();
		anonymous.remove(channel.id().asLongText());
		mobChannels.put(identity, channel);
		return true;
	}

	private static void removeMobChannel(Channel session) {
		Object identity = session.attr(ATT_IDENTITY).get();
		if (identity != null) {
			mobChannels.remove(identity);
		}
	}
	
	private static void removePcChannel(Channel session) {
		Object identity = session.attr(ATT_IDENTITY).get();
		if (identity != null) {
			mobChannels.remove(identity);
		}
	}
	
	public static void addListener(ChannelEventListener listener) {
		Type type = listener.getType();
		if (listeners.containsKey(type)) {
			throw new IllegalStateException("事件类型[" + type + "]对应的监听器已经存在");
		}
		listeners.put(listener.getType(), listener);
	}
	
	/** 发出关闭事件 */
	private static void fireClosedEvent(Object identity, int cause,
			Channel session) {
		ChannelEventListener listener = listeners.get(Type.CLOSED);
		if (listener == null) {
			return;
		}
		listener.onEvent(new ChannelEvent(cause, Type.CLOSED, identity,
				session));
	}
	
	/**发出更新channel的事件*/
	private static void fireUpdateChannelEvent(Object identity, int cause,
			Channel session) {
		ChannelEventListener listener = listeners.get(Type.REPLACED);
		if (listener == null) {
			return;
		}
		listener.onEvent(new ChannelEvent(cause, Type.REPLACED, identity,
				session));
	}
}